线程数据共享
数据(即任意的对象)在多个线程之间的共享(主要是为了解决线程间交叉执行的问题)
默认情况,线程之间抢占式执行,会出现交叉执行的情况(此处不做演示),如何实现不交叉执行,(不强调谁先执行)
public class ThreadShareDataTest {
public static void main(String[] args) {
Object object = new Object(); //此时我要让对象object 在下面两个线程之间共享,怎么做
new NewThread("线程1",object).start();
new NewThread("线程2",object).start();
}
}
class NewThread extends Thread{
Object object;
public NewThread(String name,Object object) {
super(name);
this.object = object;
}
@Override
public void run() {
for (int i = 1; i < 10; i++) {
System.out.println(getName() + object + "--->" + i);
}
}
}
输出如下
线程2java.lang.Object@13ffbf5--->1
线程2java.lang.Object@13ffbf5--->2
线程1java.lang.Object@13ffbf5--->1
线程2java.lang.Object@13ffbf5--->3
线程1java.lang.Object@13ffbf5--->2
线程2java.lang.Object@13ffbf5--->4
线程2java.lang.Object@13ffbf5--->5
线程1java.lang.Object@13ffbf5--->3
线程2java.lang.Object@13ffbf5--->6
线程1java.lang.Object@13ffbf5--->4
线程2java.lang.Object@13ffbf5--->7
线程1java.lang.Object@13ffbf5--->5
线程2java.lang.Object@13ffbf5--->8
线程1java.lang.Object@13ffbf5--->6
线程2java.lang.Object@13ffbf5--->9
线程1java.lang.Object@13ffbf5--->7
线程1java.lang.Object@13ffbf5--->8
线程1java.lang.Object@13ffbf5--->9
引入synchronized(*.class)代码块后
public class ThreadShareDataTest {
public static void main(String[] args) {
Object object = new Object(); //此时我要让对象object 在下面两个线程之间共享,怎么做
new NewThread("线程1",object).start();
new NewThread("线程2",object).start();
}
}
class NewThread extends Thread{
Object object;
public NewThread(String name,Object object) {
super(name);
this.object = object;
}
@Override
public void run() {
synchronized (Object.class) { //括号中写什么
//如果写this,代表的是什么
//同理,如果是下面这种写法,等效于什么↓↓
//如果在synchronized前加static等效于什么
//NewThread.class
//反射的相关内容?
/*
public void run() {
test();
}
synchronized void test(){
for (int i = 1; i < 10; i++) {
System.out.println(getName() + object + "--->" + i);
}
}
*/
//强调:当成对象锁的数据一定是在线程之间共享的
for (int i = 1; i < 10; i++) {
System.out.println(getName() + object + "--->" + i);
}
}
}
}
输出如下,线程不交叉执行
线程1java.lang.Object@da5a14--->1
线程1java.lang.Object@da5a14--->2
线程1java.lang.Object@da5a14--->3
线程1java.lang.Object@da5a14--->4
线程1java.lang.Object@da5a14--->5
线程1java.lang.Object@da5a14--->6
线程1java.lang.Object@da5a14--->7
线程1java.lang.Object@da5a14--->8
线程1java.lang.Object@da5a14--->9
线程2java.lang.Object@da5a14--->1
线程2java.lang.Object@da5a14--->2
线程2java.lang.Object@da5a14--->3
线程2java.lang.Object@da5a14--->4
线程2java.lang.Object@da5a14--->5
线程2java.lang.Object@da5a14--->6
线程2java.lang.Object@da5a14--->7
线程2java.lang.Object@da5a14--->8
线程2java.lang.Object@da5a14--->9
为什么通过synchronized就能实现多线程间串行运行呢?
①被synchronized括着的部分就是线程执行临界区,每次仅能有一个线程执行该临界区中的代码:当多个线程中的某个线程先拿到对象锁, 则该线程执行临界区内的代码,其他线程只能在临界区外部等待,当此线程执行完临界区中的代码后,在临界区外部等待的其他线程开始再次竞争以获取对象锁,进而执行临界区中的代码,但只能有一条线程“胜利”。
②临界区中的代码具有互斥性、唯一性和排它性:一个线程只有执行完临界区中的代码另一个线程才能执行。
synchronized方法
//声明同步方法
public synchronized void methodName( ){
//同步操作方法体
}
synchronized代码块
synchronized (需要同步操作的对象) {
//同步对象操作的语句
}
线程协作
在引入线程协作之前,思考下面一个代码
public class Watch {
String time;
class Display extends Thread{
@Override
public void run() {
System.out.println(time);
}
}
class TiMeThread extends Thread{
@Override
public void run() {
time=new java.util.Date().toString();
}
}
public static void main(String[] args) {
Watch watch=new Watch();
Display display=watch.new Display(); //内部类创建对象
display.start();
TiMeThread thread=watch.new TiMeThread();
thread.start();
}
}
输出
null
一个时间线程,一个显示器线程,如何能让时间线程先执行完,显示器线程后执行输出时间
方法① sleep()加等待时间 在显示器线程输出时间之前,如果time==null,执行sleep(时间ms)
但是性能不高,而且根据机器性能的差异,不能设置一个统一的标准,有可能时间设置短了,依旧无法输出时间。又有可能时间长了,性能降低了。
方法②join()方法 当前线程进入阻塞状态,在调用该方法的线程对象对应的线程结束后,当前线程再由阻塞转为就绪状态
public class Watch {
String time;
class Display extends Thread{
TiMeThread tiMeThread;
public Display(TiMeThread tiMeThread) {
this.tiMeThread =tiMeThread;
}
@Override
public void run() {
if (time==null) {
try {
tiMeThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(time);
}
}
class TiMeThread extends Thread{
@Override
public void run() {
time=new java.util.Date().toString();
}
}
public static void main(String[] args) {
Watch watch=new Watch();
TiMeThread tiMeThread=watch.new TiMeThread();
tiMeThread.start();
Display display=watch.new Display(tiMeThread); //内部类创建对象
display.start();
}
}
输出时间
Tue Jul 02 11:12:08 CST 2019
方法③线程协作
public class Watch {
String time;
Object lock=new Object();
class Display extends Thread{
TiMeThread tiMeThread;
public Display(TiMeThread tiMeThread) {
this.tiMeThread =tiMeThread;
}
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//wait()方法源自Object类 任何一个线程执行该方法,则该线程阻塞;
//注意①共享对象调用 ②必须位于synchronized语句块,且锁定的对象为共享对象
System.out.println(time);
}
}
class TiMeThread extends Thread{
@Override
public void run() {
time=new java.util.Date().toString();
synchronized (lock) {
lock.notify();
//notify()方法源自Object类 任何一个线程执行该方法,则之前共享对象阻塞的线程会进入就绪状态;
//注意①共享对象调用 ②必须位于synchronized语句块,且锁定的对象为共享对象
}
}
}
public static void main(String[] args) {
Watch watch=new Watch();
TiMeThread tiMeThread=watch.new TiMeThread();
tiMeThread.start();
Display display=watch.new Display(tiMeThread); //内部类创建对象
display.start();
}
}
输出
Tue Jul 02 11:24:49 CST 2019
synchronized关键字只是起到了多个线程“串行”执行临界区中代码的作用,但是哪个线程先执行,哪个线程后执行依无法确定,Object类中的wait()、notify()和notifyAll()三个方法解决了线程间的协作问题,通过这三个方法的“合理”使用可以确定多线程中线程的先后执行顺序:
①wait():对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,直到在其他线程中该对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒。
②notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程。
③notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线程;调用notifyAll()方法并不会立即激活某个等待线程,它只能撤销等待线程的中断状态,这样它们就能够在当前线程退出同步方法或同步代码块法后与其它线程展开竞争,以争取获得资源对象来执行。
一句话:谁调用了wait方法,谁就必须调用notify或notifyAll方法,并且“谁”是对象锁。
sleep和wait的区别
sleep不会释放对象锁,但是一执行wait方法,就会释放对象锁。