1、多线程概念
与操作系统中多线程差不多,但是JAVA中的多线程是对虚拟机,
而操作系统是面向实实在在的硬件(更复杂)。利用资源处理任务的思路相同。
2、此处线程
- 每个线程拥有独立的运行栈和程序计数器(PC),线程切换的开销小
- 一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一堆中分配对象,可以访问相同的变量和对象。使得线程的通信简便、高效。但多个线程操作系统共享的资源有安全隐患。(深入理解需要长期不断学习,迭代更新)
3、线程的简单创建
1、方式一
1.创建一个继承于Thread的子类
2.重写Thread的run()>>>此线程需要做的事写在此处
3.创建Thread的对象
4.通过此对象调用start()( 作用:启动线程,去调用run())
注意:
- 不能用 对象.run() 启动线程
- 一个对象只能用一次start()
创建一个线程:
线程一
package com.learn.thread;
/**继承Thresd
*/
//1.创建一个继承于Thread的子类
public class LearnThread_01 extends Thread {
//2.重写Thread的run()
@Override
public void run() {
//找出1-100的偶数
for (int i = 1; i <= 100; i++) {
if (i%2 == 0) {
System.out.println("线程一:"+i);
}
}
}
}
线程二
package com.learn.thread;
public class LearnThread_02 extends Thread{
@Override
//找出1-100 奇数
public void run() {
for (int i = 1; i <= 100; i++) {
if (i%2 != 0) {
System.out.println("线程二:"+i);
}
}
}
}
测试
package com.learn.thread;
public class AllThread {
public static void main(String[] args) {
LearnThread_01 learnThread1 = new LearnThread_01();
learnThread1.start();
//主线程
for (int i = 1; i <= 100; i++) {
if (i%2 != 0) {
System.out.println("线程二:"+i);
}
}
}
}
结果:这里已经乱了哦(体现了线程的思想,此处线程不安全)
补充1:Thread中常用方法
- satar()========================启动线程、调用run()
- run()=========================子类重写建立需要的操作
- currentThread()=================静态方法,返回执行当前代码的线程
- getName()=====================获取当前线程的名字
- setName()=====================设置当前线程的名字
- yield()========================释放当前Cpu的使用
- sleep()=======================停等(1000)单位为毫秒
- join()========================在线程A调用线程B时,A阻塞直到B完成结束阻塞
补充2:线程的优先级
0000&*&
- NAX_PRIORITY :10
- NAX_PRIORITY :5 (默认优先级)
- NAX_PRIORITY :1
&*&
setPriority();
getPriority()
2、方式二
- 创建一个实现Runnable 接口的类
- 实现抽象方法run()
- 创建实现类的对象
- 将此对象作为参数传递Thread类的构造器中,创建Thread的对象
- 通过Thread的对象调用start()
package com.learn.thread;
public class RunLearThread implements Runnable{
@Override
public void run() {
//找出1-100的偶数
for (int i = 1; i <= 100; i++) {
if (i%2 == 0) {
System.out.println("Runnable 实现");
}
}
}
}
测试
@Test
public void test_02(){
RunLearThread run = new RunLearThread();
Thread thread = new Thread(run);
thread.start();
}
结果:
4、线程安全
1、线程的生命周期(java)
2、解决简单线程安全问题
方式一:同步代码块
-
synchronized(同步监视器){
//需要同步的代码块
} -
对共享数据的操作需要同步
-
同步监视器就是锁,java中对象充当锁。多个线程只能用一把锁。
代码一
package com.security.thread;
public class Win implements Runnable{
private int ticket = 100;
int counter = 0;
Object obje = new Object();
@Override
public void run() {
//同步代码块实现线程安全
synchronized(obje){
while (true){
if (ticket >0) {
//睡
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter++;
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket+"\n"+"卖出的票数:"+counter);
ticket--;
}else {
break;
}
}
}
}
}
代码测试
package com.security.thread;
public class SealTikets {
public static void main(String[] args) {
Win win1= new Win();
Thread thread1 = new Thread(win1);
Thread thread2 = new Thread(win1);
Thread thread3 = new Thread(win1);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
测试结果
窗口1卖票,票号为:100
卖出的票数:1
窗口1卖票,票号为:99
卖出的票数:2
窗口1卖票,票号为:98
卖出的票数:3
窗口1卖票,票号为:97
卖出的票数:4
窗口1卖票,票号为:96
卖出的票数:5
窗口1卖票,票号为:95
卖出的票数:6
窗口1卖票,票号为:94
卖出的票数:7
窗口1卖票,票号为:93
卖出的票数:8
窗口1卖票,票号为:92
卖出的票数:9
.............................................................
线程已经全!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
方式二:同步方法
run 方法用 synchronized 修饰即可
package com.sec.method;
public class Wind implements Runnable{
private int ticket = 100;
int counter = 0;
//同步方法
@Override
public synchronized void run() {
while (true){
if (ticket >0) {
//睡100 毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter++;
System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+ticket+"\n"+"卖出的票数:"+counter);
ticket--;
}else {
break;
}
}
}
}
运行结果:
窗口1卖票,票号为:100
卖出的票数:1
窗口1卖票,票号为:99
卖出的票数:2
窗口1卖票,票号为:98
卖出的票数:3
窗口1卖票,票号为:97
卖出的票数:4
窗口1卖票,票号为:96
卖出的票数:5
窗口1卖票,票号为:95
卖出的票数:6
窗口1卖票,票号为:94
卖出的票数:7
窗口1卖票,票号为:93
卖出的票数:8
窗口1卖票,票号为:92
卖出的票数:9
.............................................................
小结:目前实现线程安全有四种方法