- 多线程
- 1.创建线程的4种方式
- 1.创建多线程的方式一:将一个类声明为`Thread`的子类重写run方法
- 2.创建多线程的方式二:声明实现类`Runnable`接口,并重写run方法
- 3.创建多线程的方式三:实现Callable接口 ---> JDK 5.0新增
- 4.创建多线程的方式四:使用线程池
- 2.Thread常用方法
- 3.线程安全
- 4.解决线程安全问题
- 1.synchronized锁
- 2.Lock锁
- 5.死锁
- 5.面试题
进程:电脑软件 idea mysql 每一个都属于一个进程
1.独立性
各个进程之间不受影响
2.互斥性
每个软件系统都会分配一个独立端口号,端口号一样会冲突
线程:进程是由多个或者一个线程组成的。每个进程至少得有一个线程
1.抢占式运行
线程的执行时间由系统来分配,线程切换不由线程本身决定
2.非抢占式
非抢占式让原来正在运行的进程继续运行,直至该进程完成或发生某种事件(如I/O请求),才主动放弃处理机。
线程和进程的区别:
一个线程只可以属于一个进程,但一个进程能包含多个线程
线程是进程中最小的基本单位
进程有独立性和互斥性
线程有抢占式 非抢占式
并发和并行:
并发:同时发生,轮流交替执行
并行:真正意义的同时执行
1.创建线程的4种方式 1.创建多线程的方式一:将一个类声明为Thread的子类重写run方法package com.kz.a_thread;
// 继承Thread
class My extends Thread{
//重写run方法
@Override
public void run() {
for (int i = 0; i <500 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public class Demo01 {
public static void main(String[] args) {
My my = new My();
//使线程开始执行;Java虚拟机调用这个线程的run方法。
//结果是两个线程并行运行:当前线程(从调用start方法返回)和另一个线程(执行其run方法)
my.start();
for (int i = 0; i <500 ; i++) {
System.out.println("1:"+i);
}
}
}
2.创建多线程的方式二:声明实现类Runnable接口,并重写run方法
package com.kz.b_runnable;
import static java.lang.Thread.currentThread;
// 实现Runnable接口
class My implements Runnable{
//重写run
@Override
public void run() {
//获取线程名字
String name = currentThread().getName();
for (int i = 0; i <250 ; i++) {
System.out.println(name+":"+i);
}
}
}
public class Demo01{
public static void main(String[] args) {
Thread thread = new Thread(new My());
System.out.println(thread);
new Thread(new My()).start();
new Thread(new My()).start();
new Thread(new My()).start();
}
}
开发中优先选择:实现Runnable接口的方式 原因: 1.实现的方式没有类的单继承的局限性 2.实现的方式更适合来处理多个线程有共享数据的情况3.创建多线程的方式三:实现Callable接口 —> JDK 5.0新增
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for(int i = 1;i <= 100;i++){
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大 1.call()可以有返回值的。 2.call()可以抛出异常,被外面的操作捕获,获取异常的信息 3.Callable是支持泛型的 4.需要借助FutureTask类,比如获取返回结果4.创建多线程的方式四:使用线程池
class NumberThread implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread()); //适合适用于Runnable
service.execute(new NumberThread1()); //适合适用于Runnable
// service.submit(Callable callable); //适合适用于Callable
//3.关闭连接池
service.shutdown();
}
}
好处: 1.提高响应速度(减少了创建新线程的时间) 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建) 3.便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止
创建多线程的方式有4种常用第二种
2.Thread常用方法测试Thread中的常用方法: 1.start():启动当前线程:调用当前线程的run() 2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 3.currentThredd():静态方法,返回执行当前代码的线程 4.getName():获取当前线程的名字 5.setName():设置当前线程的名字 6.yield():释放当前cpu的执行权 7.join():停止当前线程 执行分线程完毕 在执行该线程 8.stop():过时 强制停止 9.sleep(long millitme):让当前进行指定毫秒进行一一执行 10.isAlive():判断当前线程是否存活 线程的优先级: 1.MAX_PRIORITY=10 2.MIN_PRIORITY=1 3.NORM_PRIORITY=5 默认优先级 2.getPriority():获取线程优先级 3.setPriority():设置线程的优先级
并不是高优先级 一定会比低的快
3.线程安全Java是允许多线程,当多个线程操作同一个资源的时候,会导致得到或者打印的数据不准确。从而发生冲突
public class Windos {
public static void main(String[] args) {
window window=new window();
window window1=new window();
window window2=new window();
window.setName("窗口一");
window1.setName("窗口二");
window2.setName("窗口三");
window.start();
window1.start();
window2.start();
}
}
class window extends Thread{
private static int ticket=100;//静态属性
@Override
public void run() {
while (true){
if (ticket>0){
System.out.println(getName()+":"+ticket);
ticket--;
}else {break;}
}
}
}
存在线程安全问题
4.解决线程安全问题 1.synchronized锁同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明 1.操作共享数据的代码,即为需要被同步的代码 2.共享数据 多个线程共同操作的变量 3.同步监视器 锁 任何一个类的对象 都可以充当锁 4.要求 多个线程必须用同一把锁 如果是继承的多线程 需要锁 也是静态 不可用this
public class WindowTest {
public static void main(String[] args) {
A a=new A();
Thread thread=new Thread(a);
Thread thread1=new Thread(a);
Thread thread2=new Thread(a);
thread.setName("窗口1");
thread1.setName("窗口2");
thread2.setName("窗口3");
thread.start();
thread1.start();
thread2.start();
}
}
class A implements Runnable{
private int tic=100;
Object object=new Object();
@Override
public void run() {
// synchronized(this){
while (true){
synchronized(A.class){//类也是对象 类只加载一次
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tic>0)
System.out.println(Thread.currentThread().getName()+":"+tic);
tic--;
}
}
}
}
2.Lock锁
Lock锁---jdk5.0新增 1.面试题 synchronized与Lock的异同 相同:两者都可以解决线程安全问题 不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器 Lock需要手动的启动同步Lock(),结束也需要手动结束unLock()
public class LockTest {
public static void main(String[] args) {
Window w=new Window();
Thread thread=new Thread(w);
Thread thread1=new Thread(w);
Thread thread2=new Thread(w);
thread.start();
thread1.start();
thread2.start();
}
}
class Window implements Runnable{
private int id=100;
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//手动上锁
if (id>0){
System.out.println(Thread.currentThread().getName()+":"+id);
id--;
}
}finally {
lock.unlock();//手动关锁
}
}
}
}
5.死锁
演示线程的死锁
1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
2.说明:
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
我们使用同步时,要避免出现死锁。
package JavaSenior.多线程.线程安全问题;
// 懒汉式的线程安全问题
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
public Bank getInstance(){
if (instance ==null){//效率更高
synchronized (Bank.class){if (instance==null){
Bank instance=new Bank();
return instance;
}
}
}
return instance;
}
}
class ThreadTest {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
5.面试题
涉及到线程3个方法 wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 notify() 会换醒被wait的一个线程 notifyAll() 换醒所有 1.必须都写在同步代码块中 2.调用者也必须是同步监视器 定义在Java.lang.Object 面试题 sleep()和wait()的异同 相同:都可以进入阻塞状态 异同:1.两个方法声明的位置不同,Thread类中声明sleep(),Object类中声明wait() 2.调用的要求不同:sleep()可以在任何需要的场景下调用,wait必须在同步代码块中 3.如果两个方法都使用在同步代码块中,sleep不会释放锁,wait会释放锁
public class Numtest {
public static void main(String[] args) {
Num num=new Num();
Thread thread=new Thread(num);
Thread thread1=new Thread(num);
thread.start();
thread1.start();
}
}
class Num implements Runnable{
private int id=1;
public void run(){
while(true){
synchronized (this){
notify();//交替
if (id<=100){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+id);
id++;
//交替
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}



