多线程
重难点
线程的创建 4中方法 线程同步(线程安全,锁)3种方法
内存结构图:
类加载器把class文件加载进来
数据加载到内存中
方法区和堆是一个进程一份,进程的线程共享方法区和堆。资源共享带来安全隐患。虚拟机栈和程序计数器是一个线程一份,
cpu的核数从操作系统来看,看到的是物理核数的2倍。
多线程创建
方式一 继承Thread类
package com.shangguigu;
/**
* 多线程创建,方式一:继承Thread类
* 1. 创建一个子类继承于Thread类
* 2. 子类里面重写run()方法,用于执行我们需要的功能
* 3. 创建Thread子类的对象
* 4. 通过此对象调用start()
* 例子:遍历100以内的偶数---写在run方法里面
* @author wangshengnan
* @create 2020-10-02-11:02
*/
public class ThreadTest {
public static void main(String[] args) { //psvm创建main快捷键
MyThread myThread = new MyThread();//alt + enter 创建实例
myThread.start();//start()的两个作用:1 启动当前线程;2调用当前线程的run方法
//问题一:我们不能通过直接调用run()的方式启动分线程
//myThread.run();如果是调run()不调用start()也不会报错,调用run的仍然是主线程,没有启动分线程; 可以通过 打印Thread.currentThread().getName();查看详细结果;
//问题二:同一个线程只能start一次,myThread()启动了一次不能再次调用start启动,线程有状态,没启动的时候是0 ,启动之后就不再是0 ,已经启动的线程再次启动就会抛出异常 IllegalThreadState
//正确的操作是重新new一个线程对象,再去启动
//如下操作在主线程种执行
for (int i = 0; i < 500; i++) {
System.out.println(i+"*********main*********"); //i.sout打印i
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(i); //i.sout打印i
}
}
}
}
执行结果部分截图:正如结果所示,启动分线程之后主线程和分线程并发运行。
练习
package com.shangguigu.practice;
/**
* 多线程练习题: 创建两个分线程,一个遍历100以内的偶数,一个遍历100以内的奇数
* 方法一 创建两个Thread的子类 Thread1 Thread2比较麻烦
* 方法二 创建匿名子类,适用子类只用一次之后不再使用
* @author wangshengnan
* @create 2020-10-02-11:54
*/
public class ThreadPrac {
public static void main(String[] args) {
/**
* 方法一
* Thread1 thread1 = new Thread1();
* Thread2 thread2 = new Thread2();
* thread1.start();
* thread2.start();
*/
//方法二:
new Thread(){ //匿名子类,直接在里面重写run方法,实现遍历偶数
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
new Thread(){ //实现遍历奇数
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
}
}
class Thread1 extends Thread{ //方法一中的子类1
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class Thread2 extends Thread{ //方法一中的子类2
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
关于Thread类的常用方法
package com.shangguigu;
/**
* Thread类的常用方法
* 1. start()方法 用于启动分线程,并调用当前线程的run方法
* 2. run() 通常许需要重写Thread类中的此方法,将创建的线程要执行的曹祖声明在此方法中
* 3. currentThread() 是静态方法,返回执行当前代码的线程
* 4. getName() 返回当前线程的名字
* 5. setName() 设置当前线程的名字
* 6. yield() 释放此线程当前cpu的执行权,当然释放完之后cpu会调配线程,这个线程有可能再次抢到cpu的执行权。
* 7. join() 在线程a或主线程中调用线程b的join()方法,线程a就进入阻塞状态,直到线程b完全执行完之后,a才结束阻塞状态。
* 8. sleep(long millitime) 毫秒 1s = 1000mills,这里是线程进入阻塞状态,阻塞mills时间后等待cpu分配资源,分配到再执行
* 9. isAlive()
* @author wangshengnan
* @create 2020-10-02-12:15
*/
public class ThreadMethodTest {
public static void main(String[] args) {
Thread1 thread1 = new Thread1("线程:*1*");
//thread1.setName("线程一"); //修改线程的名字方法一setName
thread1.start();
Thread.currentThread().setName("主线程"); //给主线程命名
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
if(i % 2 != 0){
System.out.println(i);
}
}
if(i == 20){
try {
thread1.join(); //在主线程里调用分线程的join方法,意思是主线程进入阻塞,等待分线程完全执行完毕后解除阻塞,再去执行。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
thread1.isAlive(); //这里肯定是false 因为 上面的代码thread1已经执行完成了
}
}
}
class Thread1 extends Thread{
@Override
public void run(){ //关于抛异常,Thread1是继承Thread 如果Thread类里面run方法没有抛异常,这里也不能抛异常
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
if(i % 20 == 0){
yield(); //这里如果i能整除20 线程释放cpu,等待cpu重新分配,当然重新分配之后可能还是这个线程执行。
}
}
}
//方法二使用构造函数给线程名字,前提是Thread类里面是有给名字赋值的构造函数
public Thread1(String name){
super(name);
}
}
join()方法,意思就是红色线程不再执行,等待蓝色线程执行完成之后再去执行红色线程。
线程调度
优先级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY: 5 默认的优先级
优先级的设置
setPriority(int p)
Thread.currentThread().setPriority();
getPriority()
优先级高的抢占低优先级的,只是从概率上讲,高优先级抢占的概率更高,但是不一定就是高优先级比低优先级的先执行。
方式二 实现Runnable接口方式
静态变量是共享资源,
版权声明:本文为shengnan970116原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。