1、synchronized关键字:
1)、可用来修饰一个方法或代码块
2)、作用:保证在同一时刻只能有一个线程来访问当前对象的这段代码
2、例子(对代码块加锁,两个线程同时跑,一定要等第一个线程执行完跑完,第二个才执行):
package com.sxit.test;
public class Test implements Runnable{
public static void main(String[] args) {
Test test1 = new Test();
Thread thread1 = new Thread(test1,"线程1");
Thread thread2 = new Thread(test1,"线程2");
thread1.start();
thread2.start();
}
public void run() {
//对当前对象加锁,同一时刻只能有一个线程持有当前对象的锁,一定要等第一个线程执行完,锁释放后,第二个线程才开始跑
synchronized (this) {
for(int i = 0 ;i<50;i++){
System.out.println("当前线程是:"+Thread.currentThread().getName()+"------>>>跑到"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
-----------打印信息(太长,只截取一部分)-----------
当前线程是:线程1------>>>跑到0
当前线程是:线程1------>>>跑到1
当前线程是:线程1------>>>跑到2
当前线程是:线程1------>>>跑到3
当前线程是:线程1------>>>跑到4
当前线程是:线程1------>>>跑到5
。。。。。。。。。。。。。。。。。
当前线程是:线程1------>>>跑到49
当前线程是:线程2------>>>跑到0
当前线程是:线程2------>>>跑到1
当前线程是:线程2------>>>跑到2
当前线程是:线程2------>>>跑到3
当前线程是:线程2------>>>跑到4
当前线程是:线程2------>>>跑到5
当前线程是:线程2------>>>跑到6
。。。。。。。。。。。。。。。。。
当前线程是:线程2------>>>跑到49
3、例子(一个线程访问当前对象同步方法,另外一个线程可以访问该对象的非同步方法):
package com.sxit.test;
public class Test implements Runnable{
public static void main(String[] args) {
Test test1 = new Test();
//启一个线程
Thread thread1 = new Thread(test1,"线程1");
thread1.start();
//当前线程执行test方法
test1.test();
}
public void run() {
//对当前对象加锁,同一时刻只能有一个线程持有当前对象的锁,一定要等第一个线程执行完,锁释放后,第二个线程才开始跑
synchronized (this) {
for(int i = 0 ;i<50;i++){
System.out.println("当前线程是:"+Thread.currentThread().getName()+"------>>>跑到"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void test(){
try {
for(int i = 0 ;i<50;i++){
Thread.sleep(100);
System.out.println("当前线程是:"+Thread.currentThread().getName()+"----->>>访问非同步方法"+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
----------打印结果-------------
当前线程是:线程1------>>>跑到46
当前线程是:main----->>>访问非同步方法45
当前线程是:main----->>>访问非同步方法46
当前线程是:线程1------>>>跑到47
当前线程是:线程1------>>>跑到48
当前线程是:main----->>>访问非同步方法47
当前线程是:main----->>>访问非同步方法48
当前线程是:线程1------>>>跑到49
当前线程是:main----->>>访问非同步方法49
4、例子:(当一个线程访问synchronized声明的代码块时,别的线程访问其他synchronized代码块时将被堵塞):
package com.sxit.test;
public class Test implements Runnable {
public static void main(String[] args) {
Test test1 = new Test();
// 启一个线程
Thread thread1 = new Thread(test1, "线程1");
thread1.start();
try {
//主线程先睡一会儿,让线程1先执行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 当前主线程main执行test方法将被堵塞,知道线程1执行完释放当前对象锁才能执行test方法
test1.test();
}
public void run() {
// 对当前对象加锁,同一时刻只能有一个线程持有当前对象的锁,一定要等第一个线程执行完,锁释放后,第二个线程才开始跑
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println("当前线程是:" + Thread.currentThread().getName() + "------>>>跑到" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void test() {
synchronized (this) {
for (int i = 0; i < 50; i++) {
System.out.println("当前线程是:" + Thread.currentThread().getName() + "----->>>访问非同步方法" + i);
}
}
}
}
---------打印信息-----------
当前线程是:线程1------>>>跑到1
当前线程是:线程1------>>>跑到2
。。。。。。。。。。。。。。。。
当前线程是:线程1------>>>跑到49
当前线程是:线程main------>>>跑到1
。。。。。。。。。。。。。。。。
当前线程是:线程main------>>>跑到49
5、Thread类中start()方法源码:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the run method of this thread.
* 线程开始执行;JVM会调用该线程的run方法,也就是调用Runnable的run方法 见源码即:
* public void run() {
* //target是构造器传入的Runnable对象,如果不为空,则调用它的Run方法
* if (target != null) {
* target.run();
* }
* }
*
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start method) and the other thread (which executes its
* run method).
*
* It is never legal to start a thread more than once.
* 重复调用start方法启动线程是非法的
* In particular, a thread may not be restarted once it has completed
* execution.
* 如果线程已经执行完毕,不能再重复启动了
* 见源码started标示
*
* @exception IllegalThreadStateException if the thread was already
* started.
*/
//synchronized关键字修饰,防止同一时刻有多个线程一起启动
public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
//如果已经启动了,则started标示为true,下一次启动直接抛出异常
started = true;
//将当前线程加入到线程组中
group.add(this);
start0();
}
// native方法里执行 闭源无法看到
private native void start0();
public void run() {
if (target != null) {
target.run();
}
}
6、小结:
1)、每个类实例都对应一把锁,如果你要访问该对象的synchronized方法,你就首先得先获得这把锁,否则所属线程阻塞,方法一旦执行,就独占此锁,知道方法返回后才将锁释放,被阻塞的线程也就可以去争取这个锁了。