t1和t2两个线程
异步编程模型:t1线程执行t1的,t2执行t2的,
同步编程模型:t1线程和t2线程执行,当t1线程必须等t2线程执行结束之后,t1线程才能执行,这是同步编程模型
什么时候要同步,为什么要引入线程同步呢?
1.为了数据的安全。可以不考虑效率,因为同步机制加入后,效果等同同单线程的
2.什么条件下要使用线程同步?
第一:必须是多线程环境
第二:多线程环境共享同一个数据
第三:共享的数据涉及到修改操作
比如银行取钱的模型,不能异步进行
package 线程的同步;
/*
* 以下程序演示取款例子。以下程序不使用线程同步机制,
* 多线程同时对同一个账户进行取款操作,会出现什么问题?
*
* */
public class Test {
public static void main(String[] args) throws Exception{
//创建一个公共账户
Account ac1 = new Account("a1",5000);
//定义两个线程
Processor p1 = new Processor(ac1);
Thread t1 = new Thread(p1);
Processor p2 = new Processor(ac1);
Thread t2 = new Thread(p2);
t1.start();
//Thread.sleep(5000);
t2.start();
}
}
//准备线程
class Processor implements Runnable{
//账户
Account act;
//构造方法
Processor(Account act){
this.act = act;
}
public void run() {
System.out.println("当前余额:"+act.look());
act.withdraw(100);
System.out.println("取款成功 余额:"+act.mon);
}
}
//账户
class Account{
String name;
int mon;
public Account(String name,int mon) {
this.name = name;
this.mon = mon;
}
public void withdraw(int money) {
mon -=money;
}
public int look() {
return mon;
}
}
使用线程同步机制保证数据安全,重写以上程序
关键字:synchronized
方法(){
synchronized(共享对象){
把需要同步的代码放到同步语句块中
}
}
实现的原理:t1线程和t2线程
t1线程执行到此处,遇到了synchronized关键字,就回去找this的对象锁
如果找到this对象锁,则进入同步语句块中执行程序。当同步语句块中的代码执行结束之后,t1线程归还this的对象锁。
在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到synchronized关键字,所以也去找this的对象锁,但是该对象锁被t1线程持有,只能在这等待this对象的归还
这种方法能够精确的圈定想要同步的代码块
public void withdraw(int money) {
synchronized(this) {
mon -=money;
}
}
synchronized放在成员方法上,代表的就是当前对象,当前方法内部所有的代码块都要同步执行
public synchronized void withdraw(int money) {
mon -=money;
}
一个对象只有一个锁,如果同一个对象下,不同的方法都有synchronized关键字,那么就需要一个方法执行完把锁返回,然后新的方法才能拿锁执行下一个
但是如果同一对象下,没有加上synchronized关键字的方法,不会等待,而是直接执行
类锁
锁是类级别的,一个类只有一个
以及在静态方法上添加synchronized关键字
/*
类锁,
*/
public class Test3 {
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new process());
t1.setName("t1");
Thread t2 = new Thread(new process());
t2.setName("t2");
//类锁就一把,在静态方法中使用,一个类中有synchronized 关键字的静态方法,都需要“等”
t2.start();
t1.start();
}
}
class process implements Runnable{
/*
* Myclass ms; process(Myclass ms){ this.ms =ms; }
*/
public void run() {
if(Thread.currentThread().getName().equals("t1"))
{
Myclasss.m1();
}
else if(Thread.currentThread().getName().equals("t2")) {
Myclasss.m2();
}
}
}
class Myclasss{
public synchronized static void m1() {
try {
Thread.sleep(5000);
} catch (Exception e) {
}
System.out.println("m1.....");
}
public synchronized static void m2() {
System.out.println("m2.....");
}
}
死锁
一个死锁的写法
/*死锁*/
public class Test4 {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T1(o1,o2));
Thread t2 = new Thread(new T2(o1,o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable{
Object o1;
Object o2;
T1(Object o1,Object o2){
this.o1 = o1;
this.o2 =o2;
}
public void run() {
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
synchronized (o2) {
System.out.println("dsds");
}
}
}
}
class T2 implements Runnable{
Object o1;
Object o2;
T2(Object o1,Object o2){
this.o1 = o1;
this.o2 =o2;
}
public void run() {
synchronized (o2) {
System.out.println("dsddds");
synchronized (o1) {
System.out.println("dsddds");
}
}
}
}
守护线程
其他所有的用户线程结束,则守护线程退出!
守护线程一般都是无限执行的
void setDaemon(boolean on) 将一个用户线程转换为守护线程
以下程序可以看出如果不把线程t1转化为守护线程,那么程序将会一直执行下去,可是当将其转化为守护线程后,当主线程(用户线程)执行完毕,守护线程就会结束了
/*守护线程*/
public class Test05 {
public static void main(String[] args) {
Thread t1 = new Thread(new Proeceeewesd());
//将t1线程转变为守护线程
t1.setDaemon(true);
t1.start();
//主线程
for (int i = 0; i <10; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
class Proeceeewesd implements Runnable
{
int i=0;
public void run() {
while(true)
{
i++;
System.out.println("----->"+i);
}
}
}
定时器的应用
关于定时器的应用
作用:每隔一段固定的时间执行一段代码
java.util.Timer
Timer类下的成员方法:
从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行
void schedule(TimerTask task, Date firstTime, long period)
1.TimerTask task 这是要执行的任务代码,同时主要TimerTask是一个抽象类,不能直接实例化对象
java.lang.Object
java.util.TimerTask
TimerTask类中的抽象方法run,是子类一定要实现的,任务代码就在run方法内部去执行
2.Date firstTime 指定任务开始的时间,即到这个时间之后,开始执行定时任务里面的代码
3. long period 每执行一次TimerTask内 任务代码间隔的时间
示例程序
import java.text.SimpleDateFormat;
import java.util.*;
public class TimerTest {
public static void main(String[] args) throws Exception{
//创建定时器
Timer t =new Timer();
//指定定时任务
//从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行 。
t.schedule(new TimerNewTest(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2020-03-13 15:03:00 000"), 5*1000);
}
}
class TimerNewTest extends TimerTask
{
public void run() {
SimpleDateFormat s1 =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String f1 = s1.format(new Date());
System.out.println(f1);
}
}