Java多线程和锁

多线程

1.线程的创建

创建线程有两种方法:
1.继承Thread类,重写run方法实现。
2.创建Thread实例并且传入Runable实例,以任务的方式创建线程。
线程通过调用start方法调用run方法

// 通过继承Thread类实现,重写run方法实现多线程
public class Test {
	public static void main(String args[]) {
		MyThread t = new MyThread();
		t.start();
	}
}

class MyThread extends Thread {
	@Override
	public void run() {
		System.out.println("线程被执行");
	}
}
//  以任务的方式创建线程
public class Test {
	public static void main(String args[]) {
		MyRunable task = new MyRunable();
		Thread t = new Thread(task);
		t.start();
	}
}

class MyRunable implements Runable {
	@Override
	public void run() {
		System.out.println("线程被执行");
	}
}
2.线程的中断 interrupt

中断一个线程只需要在调用线程的interrupt方法,然后trycache线程的InterruptedException,线程种植的时候会抛出这个异常

public class Test {
	public static void main(String[] args) {
		Thread t = new Thread() {
			@Override
			public void run() {
				try {
					System.out.println("线程被执行");
				} catch (InterruptedException e) {
					System.out.println("线程中断");
				}
			}
		};
		t.start();
		t.interrupt();
	}
}
3.线程的六种状态

New:线程被创建,还未执行
Runable:运行中的线程,正在执行run方法中的代码
Blocked:运行中的线程因为某些操作被阻塞而刮挂起
Waiting:运行中的线程,因为某些操作处于等待的状态
Time Waiting:运行中的线程,因为执行sleep方法正在计时等待
Terminated:线程被终止

4.守护线程setDaemon

守护线程是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。 因此,JVM退出时,不必关心守护线程是否已结束 创建守护线程只需要调用线程的setDaemon方法。

Thread t=new Thread();
t.setDeamon(true);
t.start();

// 在守护线程中,编写代码要注意:守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。

1.同步代码块的锁机制synchronized,多个线程看同一把锁-隐式锁
public class Test {
	public static void main(String[] args) {
		Runnable run = new Ticket();
		new Thread(run).start();
	}
}

class Ticket implements Runnable {
	private int count = 10;
	private Object lock = new Object();

	@Override
	public void run() {
		while (true) {
			// 三个线程看同一把锁
			synchronized (lock) {
				if (this.count > 0) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					count--;
					System.out.println(Thread.currentThread().getName());
				} else {
					break;
				}
				System.out.println("余票" + count);
			}
		}
	}
}
2.同步方法锁,synchronized修饰方法-隐式锁
public class Test {
	public static void main(String[] args) {
		Runnable run = new Ticket();
		new Thread(run).start();
	}
}

class Ticket implements Runnable {
	private int count = 10;

	@Override
	public void run() {
		while (true) {
			Boolean sell = this.sell();
			if (!sell) {
				break;
			}

		}
	}

	// synchronized 修饰任务方法
	synchronized public Boolean sell() {
		boolean sell = true;
		if (this.count > 0) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			count--;
			System.out.println(Thread.currentThread().getName());
			sell = true;
		} else {
			sell = false;
		}
		System.out.println("余票" + count);
		return sell;
	}
}
3.显示锁-ReentrantLock,实例化一个ReentrantLock,在需要加锁的地方调用lock方法,在需要解锁的地方调用unLock方法.
public class Test {
	public static void main(String[] args) {
		Runnable run = new Ticket();
		new Thread(run).start();
	}
}

class Ticket implements Runnable {
	private int count = 10;
	// 实例化锁
	private Lock l = new ReentrantLock();

	@Override
	public void run() {
		while (true) {
			// 加锁
			l.lock();
			if (this.count > 0) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				count--;
				System.out.println(Thread.currentThread().getName());
			} else {
				break;
			}
			System.out.println("余票" + count);
			// 解锁
			l.unlock();
		}
	}
}
4.公平锁和不公平锁

公平锁:排队取锁 不公平锁:不排队,抢锁,java默认为非公平锁 设置一个锁为公平锁,只需要实例化ReentrantLock的时候传入true

public class Test {
	public static void main(String[] args) {
		// 传入true表示此锁为公平锁
		Lock lock = new ReentrantLock(true);
	}
}
5.死锁

死锁产生的条件是多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待; 避免死锁的方法是多线程获取锁的顺序要一致。

6.waitnotify用于多线程协调运行

在synchronized内部可以调用wait()使线程进入等待状态;
必须在已获得的锁对象上调用wait()方法;
在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程;
必须在已获得的锁对象上调用notify()或notifyAll()方法;
已唤醒的线程还需要重新获得锁后才能继续执行。

7.Callable带返回值的线程

public class Test {
	public static void main(String[] args) throws ExecutionException, InterruptedException {
		Call c = new Call();
		// 使用Future获取线程的返回值,线程池中提交任务后也会返回一个Future类型
		FutureTask<Integer> f = new FutureTask<>(c);
		Thread t = new Thread(f);
		t.start();
		int val = 0;
		val = f.get();
		System.out.println("返回值为" + val);
		System.out.println("同步打印");
	}
}

class Call implements Callable<Integer> {
	@Override
	public Integer call() throws Exception {
		Thread.sleep(3000);
		return 100;
	}
}

8.线程池

线程池:存放线程的容器
线程执行过程:创建线程,创建任务,执行任务,关闭线程
耗时的步骤:创建线程,关闭线程
线程池分类:缓存线程池,定长线程池,单线程线程池,周期性任务定长线程池

public class Test {
	public static void main(String[] args) throws InterruptedException {
		// 1.缓存线程池
		ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
		// 2.定长线程池
		ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
		// 3.单线程线程池
		ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

		ExecutorService service = singleThreadPool;
		// 线程池中添加并执行任务
		for (int i = 0; i < 5; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName());
				}
			});
		}
		// 祝线程sleep
		Thread.sleep(1000);
		// 下面的任务使用缓存的线程执行
		service.execute(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		});

		// 4.周期性任务定长线程池
		ScheduledExecutorService scheduleService = Executors.newScheduledThreadPool(2);
		/**
		 * 1.定制执行一次
		 * 参数1. 定是执行的任务
		 * 参数2. 时长数字
		 * 参数3. 时长数字的时间单位,TimeUnit的常量指定
		 */
		scheduleService.schedule(new Runnable() {
			@Override
			public void run() {
				System.out.println("床前明月光");
			}
		}, 5, TimeUnit.SECONDS);

		/**
		 * 周期执行的任务
		 * @params
		 * 任务
		 * 延迟时间数字(第一次执行在什么时间以后)
		 * 周期时长数字
		 * 时长数字的单位
		 */
		scheduleService.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				System.out.println("5s种后执行,每隔1s执行一次");
			}
		}, 5, 1, TimeUnit.SECONDS);
	}
}

9.lambda表达式

Lambda 表达式是一个匿名函数(对于 Java 而言并不很准确,但这里我们不纠结这个问题)。简单来说,这是一种没有声明的方法,即没有访问修饰符,返回值声明和名称。
在仅使用一次方法的地方特别有用,方法定义很短。它为我们节省了,如包含类声明和编写单独方法的工作。 Java 中的 Lambda 表达式通常使用语法是 (argument) -> (body),比如:

(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
public class Learn0304_8 {
	public static void main(String[] args) {
		Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("床前明月光");
			}
		});
		t.start();

		// lambda
		Thread t2 = new Thread(() -> System.out.println("床前明月光"));
		t2.start();

		// custom
		print(new getNumber() {
			@Override
			public int sum(int x, int y) {
				return x + y;
			}
		}, 100, 200);

		// lambda
		print((int x, int y) -> {
			return x + y;
		}, 100, 200);
	}

	static public void print(getNumber g, int x, int y) {
		System.out.println(g.sum(x, y));
	}
}

interface getNumber {
	int sum(int x, int y);
}

10.网络编程

1.tcp编程

在开发网络应用程序的时候,我们又会遇到Socket这个概念。Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络

使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。
因此,当Socket连接成功地在服务器端和客户端之间建立后:
对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号

// server
public class TestServer {
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(4000);
		// 等待客户端连接
		/*Socket socket = server.accept();
		System.out.println("客户端连接成功");
		OutputStream os = socket.getOutputStream();
		PrintStream ps = new PrintStream(os);

		ps.println("welcome connect.");

		InputStream is = socket.getInputStream();
		BufferedReader bf = new BufferedReader(new InputStreamReader(is));
		String msg = bf.readLine();
		System.out.println("服务器接收到客户端消息:" + msg);*/

		/**
		 * 多线程服务器
		 */
		while (true) {
			Socket socket = server.accept();
			new Thread() {
				@Override
				public void run() {
					try {
						// 获取输入流
						/*InputStream is = socket.getInputStream();
						BufferedReader bf = new BufferedReader(new InputStreamReader(is));
						String clientMsg = bf.readLine();*/

						// 输出流
						OutputStream os = socket.getOutputStream();
						PrintStream ps = new PrintStream(os);
						ps.println("message from server");
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

// client
public class TestClient {
	public static void main(String[] args) throws IOException {
		Socket socket = new Socket("127.0.0.1", 4000);
		InputStream is = socket.getInputStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		String msg = br.readLine();
		System.out.println("客户端接收到消息:" + msg);
		OutputStream os = socket.getOutputStream();
		PrintStream ps = new PrintStream(os);
		ps.println("message from client");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
同步Java多线程中用于保护共享资源,以确保同一时间只有一个线程可以访问该资源,从而避免数据竞争和并发问题。在Java中,可以使用synchronized关键字来实现同步的机制。 对于非静态的同步方法,可以是this对象或其他对象,要求是同一个对象。例如,使用关键字synchronized修饰的sell()方法,就在this对象上。 对于静态的同步方法,是当前类本身。因为静态方法可以在没有实例化对象的情况下使用,所以只能使用类来作为。可以使用synchronized修饰的静态方法m1()和m2()是示例。 除了直接在方法上使用synchronized关键字,还可以使用同步代码块来实现的机制。同步代码块的对象可以是this对象或其他对象。 当一个线程持有时,其他线程将无法获得该,它们将被阻塞,直到持有的线程释放的释放可以通过以下方式实现: 1. 当前线程的同步方法或同步代码块执行结束。 2. 当前线程在同步代码块或同步方法中遇到break或return语句。 3. 当前线程在同步代码块或同步方法中出现未处理的Error或Exception,导致异常结束。 4. 当前线程在同步代码块或同步方法中执行了线程对象的wait()方法,暂停当前线程,并释放。 需要注意的是,线程执行同步代码块或同步方法时,调用Thread.sleep()或Thread.yield()方法暂停当前线程的执行不会释放。此外,使用suspend()方法将线程挂起也不会释放

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值