day21
线程的优先级
1.MAX_PRIORITY:10
NORM_PRIORITY:5
MIN_PRIORITY:1
2.获取和设置当前线程的优先级
getPriority:获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但只是从概率上来讲,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程被执行完以后,低优先级的线程才被执行
方式二:实现Runnable接口
1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的对象调用start()
package shangGuiGu.day21;
//创建三个窗口卖票,总票数为100票
class window extends Thread{
private static int ticket=100;
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+":卖票,票号为"+ticket);
ticket--;
}else{
break;
}
}
}
}
public class demo02 {
public static void main(String[] args) {
window w1=new window();
window w2=new window();
window w3=new window();
w1.setName("窗口1");
w1.setName("窗口2");
w1.setName("窗口3");
w1.start();
w2.start();
w3.start();
}
}
package shangGuiGu.day21;
class mthread implements Runnable{
public void run(){
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class demo03 {
public static void main(String[] args) {
mthread mthread=new mthread();
Thread t1 = new Thread(mthread);
t1.start();
t1.setName("线程1");
Thread t2 = new Thread(mthread);
t2.start();
t2.setName("线程二");
}
}
package shangGuiGu.day21;
//创建三个窗口卖票,总票数为100票,使用实现Runnable接口的方式
class window1 implements Runnable{
private int ticket=100;
public void run(){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
ticket--;
}else{
break;
}
}
}
}
public class demo01 {
public static void main(String[] args) {
window1 w=new window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t1.setName("窗口2");
t1.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
比较创建线程的两种方式
开发中,优先选择实现Runnable接口的方式
原因:1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
线程的安全问题
1.问题:买票过程中,出现了重票、错票
2.出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
3.解决问题:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
4.在java中通过同步机制来解决线程安全问题
方式一:同步代码块
synchronize(同步监视器)
{//需要被同步的代码
}
说明:1.操作共享数据的代码即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。例如:ticket
3.同步监视器,俗称:锁。任何一个类的对象都可以充当锁。要求:多个线程必须要共用同一把锁
补充:在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步监视器
方式二:同步方法
若操作共享数据的代码完整的声明在一个方法中,不妨将此方法声明同步的
package shangGuiGu.day21;
class window2 implements Runnable{
private int ticket=100;
Object obj=new Object();
public void run(){
show();
}
private synchronized void show(){//同步监视器:this
while(true){
synchronized(obj){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
ticket--;
}
}}
}
}
public class demo04 {
public static void main(String[] args) {
window1 w=new window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t1.setName("窗口2");
t1.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
2.非静态的同步方法,同步监视器是this
静态的同步方法,同步监视器是当前类本身
死锁问题
1.理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2.说明:①出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
②使用同步时,要避免出现死锁
方法三:Lock锁
package shangGuiGu.day21;
import java.util.concurrent.locks.ReentrantLock;
class windoww implements Runnable{
private int ticket=100;
//1.实例化ReentrantLock
private ReentrantLock lock1=new ReentrantLock(true);
Object obj=new Object();
public void run(){
while(true){
try{
//调用锁定lock方法
lock1.lock();
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket);
ticket--;
}else{
break;
}
}finally {
//调用解锁方法:unlock
lock1.unlock();
}
}
}
}
public class demo07 {
public static void main(String[] args) {
window1 w=new window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t1.setName("窗口2");
t1.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
synchronized与lock方法的不同:
synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
lock需要手动的启动同步(lock),同时结束同步也需要手动的实现(unlock)
package shangGuiGu.day21;
class account{
private double balance;
public account(double balance) {
this.balance = balance;
}
//存钱
public synchronized void deposit(double amt){
if(amt>0){
balance+=amt;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"存钱成功,余额为:"+balance);
}
}
}
class customer extends Thread{
private account a1;
public customer(account acct) {
this.a1=acct;
}
public void run(){
for(int i=0;i<3;i++){
a1.deposit(1000);
}
}
}
public class demo08 {
public static void main(String[] args) {
account acct=new account(0);
customer c1 = new customer(acct);
customer c2 = new customer(acct);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}
线程的通信
涉及到的三个方法:
wait:一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify:一旦执行此方法,就会唤醒被wait的一个线程。若有多个线程被wait,就唤醒优先级高的线程
notifyall:一旦执行此方法,就会唤醒所有被wait的线程
说明:
1.三种方法必须使用在同步代码块或同步方法中。
2.三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现illegalmonitorstateexception异常
3.三个方法是定义在object类中
package shangGuiGu.day21;
class number implements Runnable{
private int number1=1;
public void run(){
while(true){
synchronized (this){
notify();
if(number1<=100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number1);
number1++;
try {
//使得调用如下wait方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}}
}
}
}
public class demo09 {
public static void main(String[] args) {
number number2 = new number();
Thread t1=new Thread(number2);
Thread t2=new Thread(number2);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}