Java多线程(一):
https://blog.youkuaiyun.com/Veer_c/article/details/103842078
Java多线程(二):
https://blog.youkuaiyun.com/Veer_c/article/details/103842263
Java多线程(三):
https://blog.youkuaiyun.com/Veer_c/article/details/103842317
Java多线程(四):
https://blog.youkuaiyun.com/Veer_c/article/details/103842602
多线程
线程是依赖于进程而存在的。
进程:正在运行的应用程序,每一正在运行的程序都会对应一个进程。
线程:进程的执行路径,执行单元
单线程和多线程的区别:
比如说有如下代码:
public class Test {
public static void main(String[] args) {
代码1;
show1();
代码2;
show2();
代码3...
}
public static void show1(){
代码11;
代码12;
}
public staic void show2(){
代码22;
代码23;
}
}
在代码执行的时候,由于多线程的存在,效率会很高
多线程的两种方案:
(1)继承Thread类:
public class MyThread extends Thread{
//1.继承Thread类
//2.重写run方法,重写run方法中的代码之后,当我们启动了这个线程之后,我们的这个线程就会执行run方法中的代码
@Override
public void run() {
//需求:开启该线程之后,执行一个for循环
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
(2)实现Runable接口:
public class MyThread implements Runnable{
//实现runnable接口
@Override
public void run() {
//启动该线程对象之后,需要执行的代码
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
多线程的问题:
启动线程我们可以利用线程对象调用start()方法
start()和run()的区别
start()是在启动线程后执行run方法里里面的代码,而直接利用调用run()方法来执行方法,是在主线程里面运行该方法
start():1.开启线程 2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程
为什么要重写run()
因为每个线程需要执行的代码都是都是不一样的,
我们需要将每个线程自己独立执行的代码写到run()方法中执行
线程不可以被多次启动,否则会出现:java.lang.IllegalThreadStateException
因为在执行同一个线程的时候,一个线程刚开始执行,但是同时又重新开始,所以会抛出异常;
注意:如果是俩个不同的线程,可以同时执行
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
public class Test {
public static void main(String[] args) {
//B:start()和run()的区别
// 创建一个线程独享
MyThread mt = new MyThread();
MyThread mt2 = new MyThread();
//mt.start();//1.首先开启了一个独立的线程 2.让这个独立的线程执行run方法中的代码
System.out.println("--------");
//mt.run();//1.普通的创对象,调方法 2.代码实在主线程执行,不会开辟新线程
//D:线程可以多次启动吗
mt.start();
//mt2.start();//如果是不同的线程对象,是可以同时开启的
//mt.start();//线程是不能多次启动的
}
}
线程的调度和控制
线程休眠(Thread.sleep(毫秒值))
线程名称(setName(),getName();)
线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
线程优先级:
当我们多个线程开始运行的时候,线程会枪战CPU的执行权,线程优先级就是你抢到CPU执行权的概率。
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//线程休眠(Thread.sleep(毫秒值))
try {
Thread.sleep(1000);//在此处出现的异常我们只能抓取,不能抛出
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取执行线程的姓名
System.out.println(this.getName()+i);
}
}
}
public class Test {
public static void main(String[] args) {
//线程名称(setName(),getName();)
//创建3个线程对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
//给三个线程设置姓名
t1.setName("刘备");
t2.setName("张飞");
t3.setName("关羽");
//设置线程的优先级
//线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
//t1.setPriority(100);//设置的区间必须在1-10之间
t1.setPriority(10);
//开启线程
t1.start();
t2.start();
t3.start();
}
}
多线程案例
1.继承Thread卖票。
public class MyThread extends Thread{
//共有100张票,将ticket改为静态之后,被类的所有对象所共享
static int ticket = 100;
@Override
public void run() {
//用一个while true循环模拟三个窗口一直处于打开的状态
while (true) {
//只有当ticket>0的时候,才可以出售票
if (ticket>0) {
System.out.println(getName()+"正在出售第:"+ticket--+"张票");
}
}
}
}
//5.1继承Thread卖票
public class Test {
public static void main(String[] args) {
//创建三个线程模拟三个售票窗口
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
//给线程设置名称
mt1.setName("窗口一");
mt2.setName("窗口二");
mt3.setName("窗口三");
//启动线程,开启售票
mt1.start();
mt2.start();
mt3.start();
}
}
2.实现Runnable卖票:
public class MyThread implements Runnable{
int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket>0) {
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
}
}
}
}
public class Test {
public static void main(String[] args) {
//创建MyThread这个对象
MyThread mt = new MyThread();
//创建3个窗口
Thread t1 = new Thread(mt);
Thread t2 = new Thread(mt);
Thread t3 = new Thread(mt);
//给线程对象设置名称
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
//启动窗口开始售票
t1.start();
t2.start();
t3.start();
}
}
注意:在实现runnable借口的时候,我们可以不用把ticket设置成static,因为在实现runnable的时候,我们本来就创建了一个实现类对象,然后将是实现类对象转换成三个不同的thread对象,所以我们的ticket本开就是共享的。
在实际生活中,我们买票的时候跟定会有时间上的延迟,所以我们给线程中加入延迟,按照真实的情景加入了延迟,却发现出现了这样的两个问题:
相同的票卖了多次:CPU的一次操作必须是原子性的(操作是CPU执行一次就可以直接完成的)
出现了负数的票:随机性和延迟导致的
出现上面的问题称为线程安全问题。
出现多线程安全问题的条件:
是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据
如何解决多线程安全问题
线程安全执行效率会有所降低
同步代码块:
synchronized(对象) {
需要被同步的代码。
}
需求:
1.测试不是同一把锁的时候线程安全吗?
答:当我们每个线程进入程序的时候,我们都会出创建一个把锁,这样执行还是会出错的。
2.如果是同一把锁线程安全吗?
如果我们利用同一把锁,则不会出现上面的问题,线程安全
1.synchronized的对象是什么
答:任意对象 ,相当于是一把锁,只要线程进去就把锁锁上
2.需要同步的代码?
答:被线程执行的代码
锁对象问题:
同步代码块(定义一个抽象类,里面专门定义一个锁),任意对象
public class MyThread implements Runnable{
//定义100张票
int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代码块
//synchronized (new Object()) {//t1,t2,t3三个线程不共享同一把锁每个线程都有自己的议案锁
synchronized (obj) {//这样3个线程才可以共享同一把锁
if (ticket>0) {
//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
try {
Thread.sleep(100);
/**
* 分析:为什么会出现两张100张票
* t1抢占到cpu的执行权,此时ticket=100,但是此刻休眠了
* 此时被t2抢占到了cpu的执行权,此时ticket=100,
* t1,t2分别睡了100毫秒之后,分别醒来了。。
* t1此时出售第100张票
* t2此时出售第100张票
*/
/**
* 分析:为什么会出现0张票和-1张票
* 假设此时票池中仅剩1张票了,
* t1进来休眠了
* t2进来休眠了
* t3进来休眠了 */
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
/* t1醒来,出售的是第1张票,此时tickt=0
* t2醒来,出售第0张票,此时ticket=-1
* t3醒来,出售第-1张票,此时ticket=-2 */
/* ticket--这个动作一共包含几步:
* 1.打印出ticket此刻本身的值
* 2.ticket自减1
* 3.将自减之后的ticket的最新的值赋值给变量ticket */
}
}
//当被同步的代码执行完毕之后,t1手里拿着的obj这个锁才会被释放,
//t1,t2,t3重新抢占cpu的执行权,谁抢到了继续拿着obj这个锁,执行同步代码块中的内容
}
}
同步方法(仅适用于实现runable接口)
public synchronized void sellTicket(){同步代码}
//this
private static synchronized void sellTicket() {
if (ticket>0) {
//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
}
}
}
将synchronized关键字加到方法上
静态同步方法
类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码
}
匿名内部类的方式使用多线程
new Thread() {
public void run() {
...
}
}.start();
new Thread(new Runnable(){
public void run() {
...
}
}).start();
利用匿名内部类启动多线程
public void run() {
while (true) {
if (x%2==0) {
synchronized (MyThread.class) {//这样3个线程才可以共享同一把锁
if (ticket>0) {
//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
}
}
Java多线程(一):
https://blog.youkuaiyun.com/Veer_c/article/details/103842078
Java多线程(二):
https://blog.youkuaiyun.com/Veer_c/article/details/103842263
Java多线程(三):
https://blog.youkuaiyun.com/Veer_c/article/details/103842317
Java多线程(四):
https://blog.youkuaiyun.com/Veer_c/article/details/103842602