线程同步需要关键字synchronized来锁资源。
有两种线程同步的方式
1.同步化方法
使用synchronized关键字修改方法来同步化对方法的访问。
当调用方法时,调用线程进入对象监视器,对象监视器锁住对象(锁住synchronized修饰的那个方法的类的当前对象)。
在对象被锁住的同时,其他线程不能进入该方法,也不能进入该对象定义的其他同步化方法,但是该对象的非同步化方法可以被访问。
当线程从方法返回时,监视器为对象解锁,允许下一个线程使用对象。
public class SynchronizedMethod {
public static void main(String[] args) {
int a[] = {1,2,3,4,5};
Thread t1 = new Thread(new MyThread(a),"Child #1");
Thread t2 = new Thread(new MyThread(a),"Child #2");
Thread t3 = new Thread(new MyThread(a),"Child #3");
Thread t4 = new Thread(new MyThread(a),"Child #4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Test{
private int sum;
//同步化方法1
synchronized int sumArray(int nums[]){
sum=0; //重置sum
for(int i=0; i< nums.length;i++){
sum+=nums[i];
System.out.println("Running total for "+Thread.currentThread().getName()+" is "+sum);
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
return sum;
}
//同步化方法2
synchronized void count1(){
int count=0;
for(int i =0; i<=20;i++){
System.out.println("Running count1 for "+Thread.currentThread().getName()+" is "+count);
count++;
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
//非同步化方法
void count2(){
int count2=0;
for(int i =0; i<=20;i++){
System.out.println("Running count2 for "+Thread.currentThread().getName()+" is "+count2);
count2++;
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
class MyThread implements Runnable{
/**
* synchronized锁的是这个Test对象sa,
* 而这个Test对象sa又是静态的,
* 所以所有的MyThread线程的对象共用一个Test的对象sa
* 当Test的对象sa被锁住了,就会互斥其他想用sa中的synchronized方法的线程
* 因此,结果能看到:
* 一旦有一个线程调用同步方法sumArray成功,锁了对象
* 其他要调用sumArray和count1同步方法的线程都要等待解锁
* 而非同步方法count2,则可在sa被锁住的时候被其他线程调用
*
*/
static Test sa = new Test();
int a[];
int answer;
MyThread(int nums[]){
a = nums;
}
public void run(){
System.out.println(Thread.currentThread().getName()+" starting.");
if(Thread.currentThread().getName().equals("Child #1")||Thread.currentThread().getName().equals("Child #2")){
answer = sa.sumArray(a);
System.out.println("Sum for "+Thread.currentThread().getName()+" is "+answer);
}
if(Thread.currentThread().getName().equals("Child #3")){
sa.count1();
}
if(Thread.currentThread().getName().equals("Child #4")){
sa.count2();
}
System.out.println(Thread.currentThread().getName() +" terminating.");
}
}
Child #1 starting.
Running total for Child #1 is 1
Child #2 starting.
Child #3 starting.
Child #4 starting.
Running count2 for Child #4 is 0
Running total for Child #1 is 3
Running count2 for Child #4 is 1
Running count2 for Child #4 is 2
Running total for Child #1 is 6
Running total for Child #1 is 10
Running count2 for Child #4 is 3
Running total for Child #1 is 15
Running count2 for Child #4 is 4
Running count2 for Child #4 is 5
Running count1 for Child #3 is 0
Sum for Child #1 is 15
Child #1 terminating.
Running count2 for Child #4 is 6
Running count1 for Child #3 is 1
Running count1 for Child #3 is 2
Running count2 for Child #4 is 7
Running count1 for Child #3 is 3
Running count2 for Child #4 is 8
Running count2 for Child #4 is 9
Running count1 for Child #3 is 4
Running count2 for Child #4 is 10
Running count1 for Child #3 is 5
Running count1 for Child #3 is 6
Running count2 for Child #4 is 11
Running count2 for Child #4 is 12
Running count1 for Child #3 is 7
Running count1 for Child #3 is 8
Running count2 for Child #4 is 13
Running count2 for Child #4 is 14
Running count1 for Child #3 is 9
Running count2 for Child #4 is 15
Running count1 for Child #3 is 10
Running count2 for Child #4 is 16
Running count1 for Child #3 is 11
Running count1 for Child #3 is 12
Running count2 for Child #4 is 17
Running count1 for Child #3 is 13
Running count2 for Child #4 is 18
Running count1 for Child #3 is 14
Running count2 for Child #4 is 19
Running count2 for Child #4 is 20
Running count1 for Child #3 is 15
Running count1 for Child #3 is 16
Child #4 terminating.
Running count1 for Child #3 is 17
Running count1 for Child #3 is 18
Running count1 for Child #3 is 19
Running count1 for Child #3 is 20
Running total for Child #2 is 1
Child #3 terminating.
Running total for Child #2 is 3
Running total for Child #2 is 6
Running total for Child #2 is 10
Running total for Child #2 is 15
Sum for Child #2 is 15
Child #2 terminating.
Process finished with exit code 0
/**
* synchronized锁的是这个Test对象sa,
* 而这个Test对象sa又是静态的,
* 所以所有的MyThread线程的对象共用一个Test的对象sa
* 当Test的对象sa被锁住了,就会互斥其他想用sa中的synchronized方法的线程
* 因此,结果能看到:
* 一旦有一个线程调用同步方法sumArray成功,锁了对象
* 其他要调用sumArray和count1同步方法的线程都要等待解锁
* 而非同步方法count2,则可在sa被锁住的时候被其他线程调用
* 如果Test对象sa不是静态的,则实现互斥锁需要static synchronized方法
*/
2.同步化语句
由同步化方法的介绍可知,同步化方法锁定对象后,该对象中的所有同步化方法都不能被其他线程使用,但是该对象中的非同步化方法还是可以被其他线程使用。
如果你想让对象被锁后,它的非synchronized修饰的方法也能被同步化呢?
这种情况一般发生在想使用由第三方创建的类,而无法访问源码时,那么在类中把synchronized加到相应的方法上是不可能的。
同步化语句使用:把对这种类定义的方法的调用放入synchronized代码块中就可以了。
synchronized(syncObject) {
//允许访问控制的代码
}
public class SynchronizedMethod {
public static void main(String[] args) {
int a[] = {1,2,3,4,5};
Thread t1 = new Thread(new MyThread(a),"Child #1");
Thread t2 = new Thread(new MyThread(a),"Child #2");
Thread t3 = new Thread(new MyThread(a),"Child #3");
Thread t4 = new Thread(new MyThread(a),"Child #4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Test{
private int sum;
int sumArray(int nums[]){
sum=0; //重置sum
for(int i=0; i< nums.length;i++){
sum+=nums[i];
System.out.println("Running total for "+Thread.currentThread().getName()+" is "+sum);
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
return sum;
}
void count1(){
int count=0;
for(int i =0; i<=20;i++){
System.out.println("Running count1 for "+Thread.currentThread().getName()+" is "+count);
count++;
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
void count2(){
int count2=0;
for(int i =0; i<=20;i++){
System.out.println("Running count2 for "+Thread.currentThread().getName()+" is "+count2);
count2++;
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
class MyThread implements Runnable{
static Test sa = new Test();
int a[];
int answer;
MyThread(int nums[]){
a = nums;
}
public void run(){
System.out.println(Thread.currentThread().getName()+" starting.");
if (Thread.currentThread().getName().equals("Child #1") || Thread.currentThread().getName().equals("Child #2")) {
synchronized(sa) {
answer = sa.sumArray(a);
System.out.println("Sum for " + Thread.currentThread().getName() + " is " + answer);
}
}
if (Thread.currentThread().getName().equals("Child #3")) {
synchronized(sa) {
sa.count1();
}
}
if (Thread.currentThread().getName().equals("Child #4")) {
sa.count2();
}
System.out.println(Thread.currentThread().getName() +" terminating.");
}
}
Child #2 starting.
Child #4 starting.
Child #3 starting.
Child #1 starting.
Running total for Child #2 is 1
Running count2 for Child #4 is 0
Running count2 for Child #4 is 1
Running total for Child #2 is 3
Running count2 for Child #4 is 2
Running total for Child #2 is 6
Running total for Child #2 is 10
Running count2 for Child #4 is 3
Running total for Child #2 is 15
Running count2 for Child #4 is 4
Running count2 for Child #4 is 5
Sum for Child #2 is 15
Child #2 terminating.
Running total for Child #1 is 1
Running count2 for Child #4 is 6
Running total for Child #1 is 3
Running total for Child #1 is 6
Running count2 for Child #4 is 7
Running total for Child #1 is 10
Running count2 for Child #4 is 8
Running total for Child #1 is 15
Running count2 for Child #4 is 9
Sum for Child #1 is 15
Child #1 terminating.
Running count2 for Child #4 is 10
Running count1 for Child #3 is 0
Running count1 for Child #3 is 1
Running count2 for Child #4 is 11
Running count2 for Child #4 is 12
Running count1 for Child #3 is 2
Running count1 for Child #3 is 3
Running count2 for Child #4 is 13
Running count1 for Child #3 is 4
Running count2 for Child #4 is 14
Running count2 for Child #4 is 15
Running count1 for Child #3 is 5
Running count2 for Child #4 is 16
Running count1 for Child #3 is 6
Running count1 for Child #3 is 7
Running count2 for Child #4 is 17
Running count1 for Child #3 is 8
Running count2 for Child #4 is 18
Running count1 for Child #3 is 9
Running count2 for Child #4 is 19
Running count2 for Child #4 is 20
Running count1 for Child #3 is 10
Child #4 terminating.
Running count1 for Child #3 is 11
Running count1 for Child #3 is 12
Running count1 for Child #3 is 13
Running count1 for Child #3 is 14
Running count1 for Child #3 is 15
Running count1 for Child #3 is 16
Running count1 for Child #3 is 17
Running count1 for Child #3 is 18
Running count1 for Child #3 is 19
Running count1 for Child #3 is 20
Child #3 terminating.
Process finished with exit code 0
count2一来就能执行是因为它不需要去获取sa对象的锁,所以它不需要等别人释放锁。