线程的同步
在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,目前实现线程同步的方法有很多,临界区对象就是其中一种。
摘自百度百科
同步的三种常用方法
1、同步代码块
- 有synchronized关键字修饰的语句块
代码如:
synchronized(object){
}
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
实例:
/**
* 线程的同步
* @author LingDu
*/
public class Demo1 extends Thread {
//定义一个可共享的count
private static int count = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
test1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 默认构造函数
public Demo1() {
super();
}
// 带参数的构造函数
public Demo1(String name) {
super(name);
}
/**
* 使用同步代码块
*/
public void test1() {
synchronized (Demo1.class) {
System.out.println(Thread.currentThread().getName() + "--->" + count);
count = count + 1;
}
}
public static void main(String[] args) {
Demo1 demo1 = new Demo1("线程1");
Demo1 demo2 = new Demo1("线程2");
Demo1 demo3 = new Demo1("线程3");
Demo1 demo4 = new Demo1("线程4");
demo1.start();
demo2.start();
demo3.start();
demo4.start();
}
}
2、同步方法
- 有synchronized关键字修饰的方法
代码如:
public synchronized void save(){}
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
/**
* 线程的同步
* @author LingDu
*/
public class Demo1 extends Thread {
//定义一个可共享的count
private static int count = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
test2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 默认构造函数
public Demo1() {
super();
}
// 带参数的构造函数
public Demo1(String name) {
super(name);
}
/**
* 使用同步方法
*/
public synchronized static void test2() {
System.out.println(Thread.currentThread().getName() + "--->" + count);
count = count + 1;
}
public static void main(String[] args) {
Demo1 demo1 = new Demo1("线程1");
Demo1 demo2 = new Demo1("线程2");
Demo1 demo3 = new Demo1("线程3");
Demo1 demo4 = new Demo1("线程4");
demo1.start();
demo2.start();
demo3.start();
demo4.start();
}
}
3、使用特殊域变量(volatile)实现线程同步
- 用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。
public class Demo2 extends Thread {
//定义一个状态,用来控制线程
private volatile boolean sign = true;
public boolean isSign() {
return sign;
}
public void setSign(boolean sign) {
this.sign = sign;
}
public Demo2() {
super();
}
public Demo2(String name) {
super(name);
}
@Override
public void run() {
// 循环10000次 ,模拟一个非常耗时的工作
for (int i = 0; i < 10000; i++) {
try {
Thread.sleep(1000);
if (sign == true) {
System.out.println(Thread.currentThread().getName() + "," + i);
} else {
System.out.println(this.getName() + "我的状态变了,我要退出了");
Thread.sleep(10000);
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 一个名字叫main的线程
Demo2 d1 = new Demo2();
Demo2 d2 = new Demo2();
d1.start();
d2.start();
// 让两个线程都去运行5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//d1线程退出
d1.setSign(false);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//d2线程退出
d2.setSign(false);
}
}
Timer类
构造方法
Timer() 创建一个新计时器。
Timer(boolean isDaemon) 创建一个新计时器,可以指定其相关的线程作为守护程序运行。
Timer(String name) 创建一个新计时器,其相关的线程具有指定的名称。
Timer(String name, boolean isDaemon) 创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行。
常用的方法:
- Void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
1、 内部类的方式实现
/**
* 创建一个定时任务
* 内部类方式实现
* 创建一个类TimeTaskDemo继承TimerTask,重写run方法
*/
public static void test1(){
//内部类方式实现
class TimeTaskDemo extends TimerTask{
@Override
public void run() {
System.out.println("1");
}
}
Timer timer = new Timer();
//schedule(TimerTask task, long delay, long period)
// 这里的0表示立即执行 , 1000表示每1秒执行一次
timer.schedule(new TimeTaskDemo(), 0,1000);
}
2、 匿名内部类实现
/**
* 匿名内部类实现
*/
public static void test2(){
Timer timer = new Timer();
//new一个TimerTask()重写
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2");
}
}, 0,1000);
}